﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using iTextSharp.text.pdf;
using System.Drawing;
using System.IO;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using iTextSharp.text;
using Clock.Hocr;
using iTextSharp.text.pdf.parser;
using System.Text.RegularExpressions;
using Clock.ImageProcessing;
using System.Diagnostics;
using System.Linq;
using Clock.Utils;
using System.Threading;

namespace Clock.Pdf
{
    public enum WriteTextMode { Line, Word }
    public enum Rotation { None = 0, Degrees90 = 90, Degrees180 = 180, Degrees270 = 270 }
    public class PDFDoc : IDisposable
    {
        public int PageCount
        {
            get
            {
                return GetReader().NumberOfPages;
            }
        }

        public string SourcePDF { get; private set; }

        public PDFDoc()
        {

        }


        public static PDFDoc Create(string fileName)
        {
            PDFDoc d = new PDFDoc();
            d.CreatePDF(fileName);

            return d;
        }
        bool NewPDF = false;
        public static PDFDoc Open(string fileName)
        {
            PDFDoc d = new PDFDoc();
            d.OpenPDF(fileName);
            return d;
        }
        public static PDFDoc Open(string fileName, string password)
        {
            PDFDoc d = new PDFDoc();
            d.OpenPDF(fileName, password);
            return d;
        }

        internal void CreatePDF(string fileName)
        {
            Document doc = new Document();
            doc.SetMargins(0, 0, 0, 0);

            var ms = GetFileStream();
            PdfWriter writer = PdfWriter.GetInstance(doc, ms);
            writer.SetMargins(0, 0, 0, 0);
            writer.CompressionLevel = 100;
            writer.SetFullCompression();
            doc.Open();
            var table = new PdfPTable(1);
            table.AddCell("");
            doc.Add(table);
            doc.Close();
            NewPDF = true;
            ReaderPDF = ms.Name;
            ms.Close();
            WriterPDF = TempData.Instance.CreateTempFile(".pdf");
            SourcePDF = fileName;

        }

        internal void OpenPDF(string fileName)
        {
            SourcePDF = fileName;
            ReaderPDF = TempData.Instance.CreateTempFile(".pdf");
            File.Copy(fileName, ReaderPDF);
        }

        byte[] Password;
        internal void OpenPDF(string sourcePdf, string ownerPassword)
        {
            SourcePDF = sourcePdf;

            Password = System.Text.ASCIIEncoding.ASCII.GetBytes(ownerPassword);
            ReaderPDF = TempData.Instance.CreateTempFile(".pdf");
            File.Copy(sourcePdf, ReaderPDF);

        }


        /// <summary>
        /// Ocr page image and return the hOcr information
        /// </summary>
        /// <param name="PageNumber"></param>
        /// <param name="ocrMode"></param>
        /// <param name="language"></param>
        /// <param name="textMode"></param>
        /// <param name="FontName"></param>
        public hDocument CreateHOCRDocument(int PageNumber, OcrMode ocrMode, string language)
        {
            string img = null;
            if (GhostScript.GhostScriptExists)
                img = GetPageImageWithGhostScript(PageNumber);
            else
                img = GetPageImage(PageNumber);

            var hocr = OcrController.CreateHOCR(OcrMode.Tesseract, language, img);
            hDocument d = new hDocument();
            d.AddFile(hocr);

            TempData.Instance.Cleanup(img);

            return d;
        }


        /// <summary>
        /// Ocr the specified page
        /// </summary>
        /// <param name="PageNumber"></param>
        /// <param name="ocrMode"></param>
        /// <param name="language"></param>
        /// <param name="textMode"></param>
        /// <param name="FontName"></param>
        public void Ocr(int PageNumber, OcrMode ocrMode, string language, WriteTextMode textMode, string FontName = null)
        {
            string img = null;
            if (GhostScript.GhostScriptExists)
                img = GetPageImageWithGhostScript(PageNumber);
            else
                img = GetPageImage(PageNumber);

            var hocr = OcrController.CreateHOCR(OcrMode.Tesseract, language, img);
            hDocument d = new hDocument();
            d.AddFile(hocr);
            AddOcrContent(d.Pages[0], PageNumber, textMode, 300);

            TempData.Instance.Cleanup(img);
            TempData.Instance.Cleanup(hocr);
        }

        /// <summary>
        /// Ocr the PDF
        /// </summary>
        /// <param name="ocrMode"></param>
        /// <param name="language"></param>
        /// <param name="textMode"></param>
        /// <param name="FontName"></param>
        public void Ocr(OcrMode ocrMode, string language, WriteTextMode textMode, string FontName = null)
        {
            for (int p = 1; p <= PageCount; p++)
            {
                try
                {
                    Ocr(p, ocrMode, language, textMode, FontName);
                }
                catch (Exception x)
                {
                    Console.WriteLine(x.Message);
                }
            }
        }

        /// <summary>
        /// Replace the document's images with compressed jBig2 images
        /// </summary>
        /// <param name="PageNumber"></param>
        public void CompressJBig2(int PageNumber)
        {
            var img = JBig2.ProcessImage(GetPageImage(PageNumber));
            ReplacePageImage(img, PageNumber);

        }

        IList<Bookmark> SingleList = new List<Bookmark>();
        public IList<Bookmark> GetBookmarks()
        {
            var r = GetReader();
            SingleList = new List<Bookmark>();
            IList<Bookmark> bookMarksList = new List<Bookmark>();
            System.Collections.Generic.IList<Dictionary<string, object>> bookmark = SimpleBookmark.GetBookmark(r);

            if (bookmark == null)
                return bookMarksList;

            Bookmark prev = null;
            foreach (Dictionary<string, object> dict in bookmark)
            {

                Bookmark b = new Bookmark();
                try
                {
                    b.StartPage = Int32.Parse(((String)dict["Page"]).Split(' ').First());
                }
                catch (Exception x)
                {
                    b.StartPage = 1;
                }
                b.Title = (String)dict["Title"].ToString();
                bookMarksList.Add(b);
                SingleList.Add(b);
                foreach (KeyValuePair<string, object> entry in dict)
                {
                    if (entry.Value is System.Collections.Generic.IList<Dictionary<string, object>>)
                    {
                        System.Collections.Generic.IList<Dictionary<string, object>> bookmarks = entry.Value as System.Collections.Generic.IList<Dictionary<string, object>>;
                        GetBookmarks(b, bookmarks);
                    }
                }
            }
            foreach (var b in bookMarksList)
            {
                SetEndPage(b);
            }
            r.Close();
            return bookMarksList;
        }

        private void SetEndPage(Bookmark b)
        {

            int? End = SingleList.Where(x => x.StartPage >= b.StartPage).OrderBy(x => x.StartPage).Select(x => x.StartPage).FirstOrDefault();

            if (End == null)
            {
                b.EndPage = PageCount;
                return;
            }

            b.EndPage = End.Value;

            foreach (var bm in b.Bookmarks)
            {
                SetEndPage(bm);
            }

        }

        private void GetBookmarks(Bookmark bm, IList<Dictionary<string, object>> bookmarks)
        {
            foreach (Dictionary<string, object> dict in bookmarks)
            {
                Bookmark b = new Bookmark();
                b.StartPage = Int32.Parse(((String)dict["Page"]).Split(' ').First());
                b.Title = (String)dict["Title"].ToString();
                bm.Bookmarks.Add(b);
                SingleList.Add(b);

                foreach (KeyValuePair<string, object> entry in dict)
                {
                    if (entry.Value is System.Collections.Generic.IList<Dictionary<string, object>>)
                    {
                        System.Collections.Generic.IList<Dictionary<string, object>> kidbookmarks = entry.Value as System.Collections.Generic.IList<Dictionary<string, object>>;
                        if (kidbookmarks != null)
                            GetBookmarks(b, kidbookmarks);
                    }
                    else
                        continue;
                }

            }
        }

        /// <summary>
        /// Compress document and replace all pages with jBig2
        /// </summary>
        /// <param name="PageNumber"></param>
        public void CompressJBig2()
        {
            var r = GetReader();
            for (int p = 1; p <= PageCount; p++)
            {
                try
                {
                    CompressJBig2(p);
                }
                catch (Exception x)
                {
                    Console.WriteLine(x.Message);
                }
            }
        }

        public void AddPdf(string pdfFile, string BookmarkDesc = null)
        {
            var bookmarks = GetBookmarks();
            var r = GetReader();
            var nr = GetReader(pdfFile);
            var mem = GetFileStream();

            Document doc = new Document();

            PdfCopy c = new PdfCopy(doc, mem);
            doc.Open();
            c.AddDocument(r);
            c.AddDocument(nr);
            c.Close();
            r.Close();
            nr.Close();


            foreach (var bookmark in bookmarks)
            {
                PdfOutline oline = new PdfOutline(c.RootOutline, PdfAction.GotoLocalPage(bookmark.StartPage, new PdfDestination(bookmark.StartPage), c), bookmark.Title);
            }
            if (BookmarkDesc != null && nr.NumberOfPages >= 1)
            {
                PdfOutline oline = new PdfOutline(c.RootOutline, PdfAction.GotoLocalPage((r.NumberOfPages + 1), new PdfDestination((r.NumberOfPages + 1)), c), BookmarkDesc);
                // AddBookmark(r.NumberOfPages + 1, BookmarkDesc);
            }
            mem = null;
            r = null;
            nr = null;
            SwapWorkingDoc();
        }
        //PdfOutline outline;
        public void AddBookmark(int StartPage, string Title)
        {
            var bookmarks = GetBookmarks();
            Bookmark b = new Bookmark();
            b.StartPage = StartPage;
            b.Title = Title;
            bookmarks.Add(b);
            var r = GetReader();

            var mem = GetFileStream(); ;

            Document doc = new Document();

            PdfCopy copy = new PdfCopy(doc, mem);
            doc.Open();
            copy.AddDocument(r);
            foreach (var bookmark in bookmarks)
            {
                PdfOutline oline = new PdfOutline(copy.RootOutline, PdfAction.GotoLocalPage(bookmark.StartPage, new PdfDestination(bookmark.StartPage), copy), bookmark.Title);
            }
            copy.Close();
            r.Close();
            mem.Close();
            mem = null;
            r = null;

            SwapWorkingDoc();

        }


        /// <summary>
        /// Replace the scanned page image with a new one.
        /// </summary>
        /// <param name="image"></param>
        /// <param name="PageNumber"></param>
        public void ReplacePageImage(System.Drawing.Image image, int PageNumber)
        {

            var ms = new MemoryStream();
            image.Save(ms, image.RawFormat);
            iTextSharp.text.Image img = iTextSharp.text.Image.GetInstance(ms.ToArray());
            ReplacePageImage(img, PageNumber);
        }

        private iTextSharp.text.Image GetImageForPDF(System.Drawing.Image image)
        {
            MemoryStream ms = new MemoryStream();
            image.Save(ms, ImageFormat.Jpeg);
            return iTextSharp.text.Image.GetInstance(ms.ToArray(), false);
        }
        string author;
        string title;
        string keywords;
        string subject;
        public void SetAuthorTitleSubjectKeywords(string Author = null, string Title = null, string Subject = null, string Keywords = null)
        {
            author = Author;
            title = Title;
            keywords = Keywords;
            subject = Subject;
            var r = GetReader();
            var mem = GetFileStream(); ;
            Document doc = new Document();

            PdfCopy c = new PdfCopy(doc, mem);
            doc.Open();

            c.CloseStream = false;
            for (int i = 1; i <= r.NumberOfPages; i++)
            {
                doc.SetPageSize(r.GetPageSizeWithRotation(i));
                doc.NewPage();
                var importedPage = c.GetImportedPage(r, i);
                c.AddPage(importedPage);
            }
            c.Outlines = SimpleBookmark.GetBookmark(r);
            if (Author != null)
                doc.AddAuthor(Author);
            if (Title != null)
                doc.AddTitle(Title);
            if (Subject != null)
                doc.AddSubject(Subject);
            if (Keywords != null)
                doc.AddKeywords(Keywords);

            doc.Close();
            c.Close();
            r.Close();
            mem.Close();
            mem = null;
            r = null;
            SwapWorkingDoc();
        }

        public void AddPage(iTextSharp.text.Image image, iTextSharp.text.Rectangle pageSize, Rotation Rotation = Rotation.None)
        {
            var r = GetReader();
            var mem = GetFileStream();
            Document doc = new Document();
            PdfWriter writer = PdfWriter.GetInstance(doc, mem);

            doc.SetMargins(0, 0, 0, 0);
            writer.SetMargins(0, 0, 0, 0);
            writer.CompressionLevel = 100;
            writer.SetFullCompression();
            doc.Open();

            PdfContentByte cb = writer.DirectContent;
            int startPage = NewPDF ? 2 : 1;
            for (int i = startPage; i <= r.NumberOfPages; i++)
            {
                doc.SetPageSize(r.GetPageSizeWithRotation(i));
                doc.NewPage();
                var page = writer.GetImportedPage(r, i);
                int rot = r.GetPageRotation(i);

                if (rot == 90 || rot == 270)
                {
                    cb.AddTemplate(page, 0, -1.0F, 1.0F, 0, 0, r.GetPageSizeWithRotation(i).Height);
                }
                else
                {
                    cb.AddTemplate(page, 1.0F, 0, 0, 1.0F, 0, 0);
                }
            }
            float width = (float)((image.Width / 300.00) * 72.00);
            float height = (float)((image.Height / 300.00) * 72.00);
            iTextSharp.text.Rectangle rect = new iTextSharp.text.Rectangle(width, height);

            if (pageSize == null)
            {
                doc.SetPageSize(rect);
            }
            else
                doc.SetPageSize(pageSize);

            image.SetAbsolutePosition((doc.PageSize.Width - width) / 2, doc.PageSize.Height - height);
            image.ScaleAbsolute(width, height);
            writer.Outlines = SimpleBookmark.GetBookmark(r);

            doc.NewPage();
            doc.Add(image);
            doc.AddAuthor(author);
            doc.AddKeywords(keywords);
            doc.AddSubject(subject);
            doc.AddTitle(title);
            doc.Close();
            writer.Close();
            r.Close();
            mem.Close();
            mem = null;
            r = null;
            NewPDF = false;
            SwapWorkingDoc();
        }

        private void SwapWorkingDoc()
        {
            GC.Collect();
            string writerPDF = ReaderPDF;
            string readerPDF = WriterPDF;
            WriterPDF = writerPDF;
            ReaderPDF = readerPDF;
        }

        public void AddPage(string imagePath, iTextSharp.text.Rectangle pageSize)
        {
            System.Drawing.Image image = System.Drawing.Image.FromFile(imagePath);
            var img = this.GetImageForPDF(image);
            AddPage(img, pageSize);
        }

        public void AddPage(System.Drawing.Image i)
        {
            var img = this.GetImageForPDF(i);
            AddPage(img, null);
        }

        public void AddPage(System.Drawing.Image i, iTextSharp.text.Rectangle pageSize)
        {
            var img = this.GetImageForPDF(i);
            AddPage(img, pageSize);
        }

        public void AddPage(System.Drawing.Bitmap i)
        {
            var img = this.GetImageForPDF(i);
            AddPage(img, null);
        }

        public void AddPage(System.Drawing.Bitmap i, iTextSharp.text.Rectangle pageSize)
        {
            var img = this.GetImageForPDF(i);
            AddPage(img, pageSize);
        }

        public void AddPage(string imagePath)
        {
            System.Drawing.Image image = System.Drawing.Image.FromFile(imagePath);
            AddPage(image);
        }

        public void AddPage(iTextSharp.text.Image image, iTextSharp.text.Rectangle pageSize, int Rotation)
        {
            AddPage(image, pageSize);
            Rotate(GetReader().NumberOfPages, Rotation);
        }

        public void AddPage(string imagePath, iTextSharp.text.Rectangle pageSize, Rotation Rotation)
        {
            System.Drawing.Image image = System.Drawing.Image.FromFile(imagePath);
            var img = this.GetImageForPDF(image);
            AddPage(img, pageSize, (int)Rotation);
        }

        public void AddPage(System.Drawing.Image i, Rotation Rotation)
        {
            var img = this.GetImageForPDF(i);
            AddPage(img, null, (int)Rotation);
        }

        public void AddPage(System.Drawing.Image i, iTextSharp.text.Rectangle pageSize, Rotation Rotation)
        {
            var img = this.GetImageForPDF(i);
            AddPage(img, pageSize, (int)Rotation);
        }

        public void AddPage(System.Drawing.Bitmap i, Rotation Rotation)
        {
            var img = this.GetImageForPDF(i);
            AddPage(img, null, (int)Rotation);
        }

        public void AddPage(System.Drawing.Bitmap i, iTextSharp.text.Rectangle pageSize, Rotation Rotation)
        {
            var img = this.GetImageForPDF(i);
            AddPage(img, pageSize, (int)Rotation);
        }

        public void AddPage(string imagePath, Rotation Rotation)
        {
            System.Drawing.Image image = System.Drawing.Image.FromFile(imagePath);
            AddPage(image, Rotation);
        }

        /// <summary>
        /// Replace the page image with the given iTextsharp image object
        /// </summary>
        /// <param name="image"></param>
        /// <param name="PageNumber"></param>
        public void ReplacePageImage(iTextSharp.text.Image image, int PageNumber)
        {
            var r = GetReader();
            var mem = GetFileStream(); ;

            PdfStamper pdfStamper = new PdfStamper(r, mem);
            PdfWriter writer = pdfStamper.Writer;

            PdfDictionary pg = r.GetPageN(PageNumber);
            PdfDictionary res =
              (PdfDictionary)iTextSharp.text.pdf.PdfReader.GetPdfObject(pg.Get(PdfName.RESOURCES));
            PdfDictionary xobj =
              (PdfDictionary)iTextSharp.text.pdf.PdfReader.GetPdfObject(res.Get(PdfName.XOBJECT));
            if (xobj != null)
            {
                foreach (PdfName name in xobj.Keys)
                {
                    PdfObject obj = xobj.Get(name);
                    if (obj.IsIndirect())
                    {
                        PdfDictionary tg = (PdfDictionary)iTextSharp.text.pdf.PdfReader.GetPdfObject(obj);
                        PdfName type =
                          (PdfName)iTextSharp.text.pdf.PdfReader.GetPdfObject(tg.Get(PdfName.SUBTYPE));
                        if (PdfName.IMAGE.Equals(type))
                        {
                            iTextSharp.text.pdf.PdfReader.KillIndirect(obj);
                            // tg.Remove(type);
                            iTextSharp.text.Image maskImage = image.ImageMask;
                            if (maskImage != null)
                                writer.AddDirectImageSimple(maskImage);

                            writer.AddDirectImageSimple(image, (PdfIndirectReference)obj);

                            break;
                        }
                    }
                }
            }
            pdfStamper.Close();
            r.RemoveUnusedObjects();
            r.Close();
            mem = null;
            r = null;
            SwapWorkingDoc();
            // iTextReader = new iTextSharp.text.pdf.PdfReader(ms);
        }

        /// <summary>
        /// Stamp existing PDF with OCR content without replacing the image
        /// </summary>
        /// <param name="doc"></param>
        /// <param name="settings"></param>
        /// <returns></returns>
        public void ApplyHOCR(string hocrDoc, WriteTextMode writeMode, int Dpi, string FontName = null)
        {
            hDocument doc = new hDocument();
            doc.AddFile(hocrDoc);

            int pageNumber = 1;
            foreach (hPage hpage in doc.Pages) // Must start at 1 because 0 is not an actual page.
            {
                AddOcrContent(hpage, pageNumber, writeMode, Dpi, FontName);
                pageNumber += 1;
            }
        }

        /// <summary>
        /// Stamp existing PDF with OCR content without replacing the image
        /// </summary>
        /// <param name="hpage">hPage containing word locations for the given page number</param>
        /// <param name="pageNumber">page number to write the under text to</param>
        /// <param name="writeMode">Line or Word</param>
        /// <param name="Dpi">Dpi of the scanned image (used to accurately calculate word locations)</param>

        public void AddOcrContent(hPage hpage, int pageNumber, WriteTextMode writeMode, int Dpi, string FontName = null)
        {
            var r = GetReader();
            var mem = GetFileStream();
            // iTextSharp.text.pdf.PdfReader r = new iTextSharp.text.pdf.PdfReader(TempFile);
            PdfStamper pdfStamper = new PdfStamper(r, mem);
            PdfImportedPage page = pdfStamper.GetImportedPage(r, pageNumber);
            foreach (hParagraph para in hpage.Paragraphs)
            {
                foreach (hLine line in para.Lines)
                {
                    if (writeMode == WriteTextMode.Word)
                    {
                        line.AlignTops();

                        foreach (hWord c in line.Words)
                        {
                            c.CleanText();

                            BBox b = BBox.ConvertBBoxToPoints(c.BBox, Dpi);

                            if (b.Height > 50)
                                continue;
                            PdfContentByte cb = pdfStamper.GetUnderContent(pageNumber);

                            BaseFont base_font = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.WINANSI, false);

                            iTextSharp.text.Font font = new iTextSharp.text.Font(base_font);
                            if (FontName != null && FontName != string.Empty)
                            {
                                var fontPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Fonts), FontName);
                                base_font = BaseFont.CreateFont(fontPath, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
                                // BaseFont base_font = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.WINANSI, false);
                                font = new iTextSharp.text.Font(base_font);
                            }

                            cb.BeginText();


                            float size = 0;// Math.Round(b.Height);
                            while (1 == 1 && size < 50)
                            {
                                var width = base_font.GetWidthPoint(c.Text, size);
                                if (width < b.Width)
                                {
                                    size += 1;
                                }
                                else
                                    break;
                            }
                            if (size < 10)
                                size = size - 1;

                            if (size == 0)
                                size = 1;

                            cb.SetFontAndSize(base_font, b.Height >= 2 ? (int)size : 2);
                            cb.SetTextMatrix(b.Left, page.Height - b.Top - b.Height);
                            cb.SetWordSpacing(PdfWriter.SPACE);

                            cb.ShowText(c.Text + " ");
                            cb.EndText();
                        }
                    }

                    if (writeMode == WriteTextMode.Line)
                    {
                        line.CleanText();
                        BBox b = BBox.ConvertBBoxToPoints(line.BBox, Dpi);
                        line.AlignTops();
                        if (b.Height > 28)
                            continue;

                        BBox lineBox = BBox.ConvertBBoxToPoints(line.BBox, Dpi);
                        PdfContentByte cb = cb = pdfStamper.GetUnderContent(pageNumber);

                        BaseFont base_font = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.WINANSI, false);
                        iTextSharp.text.Font font = new iTextSharp.text.Font(base_font);

                        cb.BeginText();

                        float size = 0;
                        while (1 == 1 && size < 50)
                        {
                            var width = base_font.GetWidthPoint(line.Text, size);
                            if (width < b.Width)
                            {
                                size += 1;
                            }
                            else
                                break;
                        }

                        cb.SetFontAndSize(base_font, b.Height >= 2 ? (int)size : 2);

                        cb.SetTextMatrix(b.Left, page.Height - b.Top - b.Height + 2);
                        cb.SetWordSpacing(.25f);
                        cb.ShowText(line.Text);
                        cb.EndText();
                    }


                }
            }


            r.RemoveUnusedObjects();

            pdfStamper.Close();
            pdfStamper.Reader.Close();
            mem.Close();
            mem = null;
            r = null;
            SwapWorkingDoc();


        }

        public void Save(string newFile, string password = null)
        {
            // pdfStamper.Close();
            //  iTextReader.Close();
            var mem = GetFileStream();
            var saveToFS = new FileStream(newFile, FileMode.Create);
            var r = GetReader();
            if (password != null)
            {
                PdfEncryptor.Encrypt(
                  r,
                  saveToFS,
                  true,
                  null,
                  password,
                  PdfWriter.ALLOW_PRINTING | PdfWriter.ALLOW_COPY
                );
            }
            else
            {
                mem.CopyTo(saveToFS);
            }

            mem.Close();
            mem = null;
            //    File.Copy(ReaderPDF, newFile);
        }

        public void Save()
        {

            File.Copy(ReaderPDF, SourcePDF, true);

        }

        public byte[] PDFBytes
        {
            get
            {
                return File.ReadAllBytes(ReaderPDF);
            }
        }

        public void SplitByBookmark(string outPutFolder, int[] PagesToIgnore = null)
        {
            var b = GetBookmarks();

            foreach (var bm in b)
            {
                bool extract = true;
                if (PagesToIgnore != null)
                    foreach (var i in PagesToIgnore)
                        if (bm.StartPage == i)
                            extract = false;

                if (extract)
                    SplitByBookmark(bm, outPutFolder, PagesToIgnore);
            }
        }

        public void SplitByBookmark(string outPutFolder, IList<Bookmark> Bookmarks, int[] PagesToIgnore)
        {
            var b = Bookmarks;

            foreach (var bm in b)
            {
                bool extract = true;
                if (PagesToIgnore != null)
                    foreach (var i in PagesToIgnore)
                        if (bm.StartPage == i)
                            extract = false;

                if (extract)
                    SplitByBookmark(bm, outPutFolder, PagesToIgnore);
            }
        }

        public void SplitByBookmark(string outPutFolder, Bookmark Bookmark)
        {
            SplitByBookmark(Bookmark, outPutFolder, null);
        }

        private void SplitByBookmark(Bookmark bm, string outPutFolder, int[] PagesToIgnore)
        {
            string invalidChars = Regex.Escape(new string(Path.GetInvalidFileNameChars()));
            string invalidReStr = string.Format(@"[{0}]+", invalidChars);
            string fileName = Regex.Replace(bm.Title, invalidReStr, "_");

            fileName += ".pdf";

            bool extract = true;
            if (PagesToIgnore != null)
                foreach (var i in PagesToIgnore)
                    if (bm.StartPage == i)
                        extract = false;

            if (extract)
                ExtractPages(Path.Combine(outPutFolder, fileName), bm.StartPage, bm.EndPage);

            foreach (var b in bm.Bookmarks)
            {
                SplitByBookmark(b, Path.Combine(outPutFolder, b.Title), PagesToIgnore);
            }
        }

        public enum SearchMethod { All, Any }

        public IList<HOcrClass> Search(int PageNumber, string[] keyWords, SearchMethod method)
        {
            bool found = false;
            var locs = GetWordLocations(PageNumber);
            IList<HOcrClass> Results = new List<HOcrClass>();
            foreach (string keyWord in keyWords)
            {
                var res = locs.Where(x => x.Text.ToLower().Contains(keyWord.ToLower())).ToList<HOcrClass>();
                foreach (var r in res)
                    Results.Add(r);

                if (res.Count > 0)
                    found = true;
                else
                {
                    found = false;

                    if (method == SearchMethod.All)
                    {
                        Results.Clear();
                        break;
                    }
                }
            }

            if (found)
                return Results;
            return null;
        }

        public bool Search(string PageText, string[] keyWords, SearchMethod method)
        {
            bool found = false;
            string pageText = PageText;
            foreach (string keyWord in keyWords)
            {
                if (pageText.ToLower().Contains(keyWord.ToLower()))
                    found = true;
                else
                {
                    found = false;
                    return found;
                }

            }
            return found;
        }

        public void ExtractPages(string outputPdfPath, int startRange, int endRage)
        {
            var r = GetReader();
            // create new pdf of pages in the extractPages list
            Document document = new Document();
            if (endRage < startRange)
                endRage = startRange;
            using (MemoryStream memoryStream = new MemoryStream())
            {
                PdfWriter writer = PdfWriter.GetInstance(document, memoryStream);
                document.Open();
                document.AddDocListener(writer);
                for (int p = 1; p <= PageCount; p++)
                {
                    if (p < startRange || p > endRage)
                        continue;

                    document.SetPageSize(r.GetPageSizeWithRotation(p));
                    document.NewPage();
                    PdfContentByte cb = writer.DirectContent;
                    PdfImportedPage pageImport = writer.GetImportedPage(r, p);
                    int rot = r.GetPageRotation(p);
                    if (rot == 90 || rot == 270)
                    {
                        cb.AddTemplate(pageImport, 0, -1.0F, 1.0F, 0, 0, r.GetPageSizeWithRotation(p).Height);
                    }
                    else
                    {
                        cb.AddTemplate(pageImport, 1.0F, 0, 0, 1.0F, 0, 0);
                    }
                }
                document.Close();
                r.Close();
                r = null;
                File.WriteAllBytes(outputPdfPath, memoryStream.ToArray());
            }
        }

        private static void extractBookmarkToPDF(iTextSharp.text.pdf.PdfReader reader, int pageFrom, int pageTo, String outputName, String outputFolder)
        {
            Document document = new Document();
            try
            {
                // Create a writer for the outputstream
                PdfWriter writer = PdfWriter.GetInstance(document, new FileStream(outputFolder + outputName, FileMode.Create));
                document.Open();
                PdfContentByte cb = writer.DirectContent; // Holds the PDF data
                PdfImportedPage page;

                while (pageFrom <= pageTo)
                {
                    if (pageFrom > pageTo)
                        break;

                    document.NewPage();
                    page = writer.GetImportedPage(reader, pageFrom);

                    cb.AddTemplate(page, 0, 0);

                    //document.SetPageSize(new iTextSharp.text.Rectangle(page.Width, page.Height));
                    pageFrom++;
                }
                document.Close();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            finally
            {
                if (document.IsOpen())
                    document.Close();
            }
        }

        public string GetText(int PageNumber)
        {

            PdfTextExtractionStrategy strat = new PdfTextExtractionStrategy();
            string te = PdfTextExtractor.GetTextFromPage(GetReader(), PageNumber, strat);


            return te;

        }

        public string GetText()
        {
            var r = GetReader();
            PdfTextExtractionStrategy strat = new PdfTextExtractionStrategy();
            StringBuilder sb = new StringBuilder();
            for (int i = 1; i <= PageCount; i++)
            {

                string te = PdfTextExtractor.GetTextFromPage(r, i, strat);
                sb.Append(te);

            }
            r.Close();
            r = null;
            return sb.ToString();
        }


        /// <summary>
        /// Watermark a PDF.
        /// </summary>
        /// <param name="waterMarkText"></param>
        /// <returns>Newly watermarked PDF in bytes</returns>
        public void Watermark(int PageNumber, string waterMarkText)
        {
            var r = GetReader();
            var mem = GetFileStream(); ;
            PdfStamper pdfStamper = new PdfStamper(r, mem);
            PdfDictionary page = r.GetPageN(PageNumber);
            //Get the raw content
            PdfArray contentarray = page.GetAsArray(PdfName.CONTENTS);
            if (contentarray != null)
            {
                //Loop through content
                for (int j = 0; j < contentarray.Size; j++)
                {
                    //Get the raw byte stream
                    PRStream stream = (PRStream)contentarray.GetAsStream(j);
                    //Convert to a string. NOTE, you might need a different encoding here
                    String content = System.Text.Encoding.ASCII.GetString(iTextSharp.text.pdf.PdfReader.GetStreamBytes(stream));
                    //Look for the OCG token in the stream as well as our watermarked text
                    if (content.IndexOf(waterMarkText) >= 0)
                    {
                        //Remove it by giving it zero length and zero data
                        stream.Put(PdfName.LENGTH, new PdfNumber(0));
                        stream.SetData(new byte[0]);
                    }
                }
            }

            iTextSharp.text.Rectangle pageSize = r.GetPageSizeWithRotation(PageNumber);
            PdfContentByte pdfPageContents = pdfStamper.GetOverContent(PageNumber);

            PdfGState gstate = new PdfGState();
            gstate.FillOpacity = 0.5f;
            gstate.StrokeOpacity = 0.5f;

            pdfPageContents.SaveState();
            pdfPageContents.SetGState(gstate);

            pdfPageContents.BeginText(); // Start working with text.
            BaseFont baseFont = BaseFont.CreateFont(BaseFont.HELVETICA_BOLD, Encoding.ASCII.EncodingName, false);
            pdfPageContents.SetFontAndSize(baseFont, 120); // 40 point font

            pdfPageContents.SetRGBColorFill(Color.DarkGray.R, Color.DarkGray.G, Color.DarkGray.B); // Sets the color of the font, RED in this instance
            float textAngle =
                (float)GetHypotenuseAngleInDegreesFrom(pageSize.Height, pageSize.Width);

            pdfPageContents.ShowTextAligned(PdfContentByte.ALIGN_CENTER, waterMarkText,
                           pageSize.Width / 2,
                           pageSize.Height / 2 + 600,
                           textAngle);

            pdfPageContents.ShowTextAligned(PdfContentByte.ALIGN_CENTER, waterMarkText,
                                            pageSize.Width / 2,
                                            pageSize.Height / 2 + 200,
                                            textAngle);

            pdfPageContents.ShowTextAligned(PdfContentByte.ALIGN_CENTER, waterMarkText,
                                            pageSize.Width / 2,
                                            pageSize.Height / 2 - 200,
                                            textAngle);

            pdfPageContents.ShowTextAligned(PdfContentByte.ALIGN_CENTER, waterMarkText,
                                     pageSize.Width / 2,
                                     pageSize.Height / 2 - 600,
                                     textAngle);


            pdfPageContents.EndText(); // Done working with text
            pdfStamper.SetFullCompression();
            pdfStamper.Close();
            r.Close();
            mem.Close();
            mem = null;
            r = null;
            SwapWorkingDoc();
            // iTextReader = new iTextSharp.text.pdf.PdfReader(ms);
        }

        public void Watermark(string waterMarkText)
        {
            for (int i = 1; i <= PageCount; i++) // Must start at 1 because 0 is not an actual page.
            {
                Watermark(i, waterMarkText);
            }
        }

        static double GetHypotenuseAngleInDegreesFrom(double opposite, double adjacent)
        {
            //http://www.regentsprep.org/Regents/Math/rtritrig/LtrigA.htm
            // Tan <angle> = opposite/adjacent
            // Math.Atan2: http://msdn.microsoft.com/en-us/library/system.math.atan2(VS.80).aspx 

            double radians = Math.Atan2(opposite, adjacent); // Get Radians for Atan2
            double angle = radians * (180 / Math.PI); // Change back to degrees
            return angle;
        }

        public byte[] ExtractPages(int startPage, int endPage, string password)
        {
            var r = GetReader();
            // create new pdf of pages in the extractPages list
            Document document = new Document();
            using (MemoryStream memoryStream = new MemoryStream())
            {
                PdfWriter writer = PdfWriter.GetInstance(document, memoryStream);
                document.Open();
                document.AddDocListener(writer);
                for (int p = startPage; p <= endPage; p++)
                {
                    document.SetPageSize(r.GetPageSizeWithRotation(p));
                    document.NewPage();
                    PdfImportedPage importedPage = writer.GetImportedPage(r, p);
                    int rot = r.GetPageSizeWithRotation(p).Rotation;

                    var pageRotation = r.GetPageRotation(p);
                    var pageWidth = r.GetPageSizeWithRotation(p).Width;
                    var pageHeight = r.GetPageSizeWithRotation(p).Height;
                    switch (pageRotation)
                    {
                        case 0:
                            writer.DirectContent.AddTemplate(importedPage, 1f, 0, 0, 1f, 0, 0);
                            break;

                        case 90:
                            writer.DirectContent.AddTemplate(importedPage, 0, -1f, 1f, 0, 0, pageHeight);
                            break;

                        case 180:
                            writer.DirectContent.AddTemplate(importedPage, -1f, 0, 0, -1f, pageWidth, pageHeight);
                            break;

                        case 270:
                            writer.DirectContent.AddTemplate(importedPage, 0, 1f, -1f, 0, pageWidth, 0);
                            break;

                        default:
                            throw new InvalidOperationException(string.Format("Unexpected page rotation: [{0}].", pageRotation));
                    }

                    //// cb.AddTemplate(pageImport, 1.0F, 0, 0, 1.0F, 0, 0);
                    //if (rot == 90 || rot == 270)
                    //{

                    //    cb.AddTemplate(pageImport, 0, -1.0F, 1.0F, 0, 0, r.GetPageSizeWithRotation(p).Height);
                    //}
                    //else
                    //{
                    //    cb.AddTemplate(pageImport, 1.0F, 0, 0, 1.0F, 0, 0);
                    //}
                }
                document.Close();
                r.Close();
                r = null;
                return memoryStream.ToArray();
            }
        }

        private string GetPageImageWithGhostScript(int PageNumber)
        {
            GhostScript g = new GhostScript();
            string imgFile = g.ConvertPDFToBitmap(ReaderPDF, PageNumber, PageNumber);
            return imgFile;
        }

        public string GetPageImage(int PageNumber)
        {
            if (GhostScript.GhostScriptExists)
                return GetPageImageWithGhostScript(PageNumber);

            var r = GetReader();
            PdfReaderContentParser parser = new PdfReaderContentParser(r);
            MyImageRenderListener listener = new MyImageRenderListener();
            listener.PageNumber = PageNumber;
            listener.Reader = this;

            try
            {

                parser.ProcessContent(PageNumber, listener);

                if (listener.ParsedImages.Count > 1 && GetText(PageNumber).Trim() != string.Empty)
                    return null;

                if (listener.ParsedImages.Count > 0)
                {
                    //   Dpi = (int)Math.Ceiling(listener.ParsedImages[0].Image.HorizontalResolution + listener.ParsedImages[0].Image.VerticalResolution);
                    string s = TempData.Instance.CreateTempFile(".bmp");
                    var bmp = ImageProcessor.ConvertToImage(listener.ParsedImages[0].Image, "BMP", 100, 300);

                    bmp.Save(s);

                    return s;
                }

            }
            catch (Exception x)
            {
                throw x;

            }
            finally
            {
                r.Close();
                r = null;
            }
            return null;
        }


        public void Dispose()
        {
            TempData.Instance.Cleanup();
        }


        public void Rotate(int PageNumber, Rotation Degrees)
        {
            Rotate(PageNumber, (int)Degrees);
        }

        public void Rotate(Rotation Degrees)
        {
            Rotate((int)Degrees);
        }


        FileStream currentFileStream;
        private FileStream GetFileStream()
        {
            if (File.Exists(WriterPDF) == false)
                WriterPDF = TempData.Instance.CreateTempFile(".pdf");

            if (currentFileStream != null)
            {
                currentFileStream.Close();
                currentFileStream = null;
            }

            currentFileStream = new FileStream(WriterPDF, FileMode.Create, FileAccess.ReadWrite);

            return currentFileStream;
        }
        string ReaderPDF;
        string WriterPDF;

        private iTextSharp.text.pdf.PdfReader GetReader()
        {
            return GetReader(ReaderPDF);
        }

        private iTextSharp.text.pdf.PdfReader GetReader(string pdfFile)
        {
            if (Password != null)
                return new iTextSharp.text.pdf.PdfReader(pdfFile, Password);

            return new iTextSharp.text.pdf.PdfReader(pdfFile);
        }

        public void Rotate(int PageNumber, int Degrees)
        {
            var r = GetReader();

            var p = r.GetPageN(PageNumber);
            int rotation = r.GetPageSizeWithRotation(PageNumber).Rotation;
            p.Put(PdfName.ROTATE, new PdfNumber(Degrees));
            var mem = GetFileStream();
            PdfStamper Stamper = new PdfStamper(r, mem);
            Stamper.Outlines = SimpleBookmark.GetBookmark(r);
            Stamper.Close();
            r.Close();
            r = null;
            mem.Close();
            mem = null;
            SwapWorkingDoc();

        }

        /// <summary>
        /// Rotation all pages
        /// </summary>
        /// <param name="Degrees"></param>
        public void Rotate(int Degrees)
        {
            var r = GetReader();
            var mem = GetFileStream(); ;
            PdfStamper Stamper = new PdfStamper(r, mem);

            for (int i = 1; i <= r.NumberOfPages; i++)
            {
                var p = r.GetPageN(i);
                int rotation = p.GetAsNumber(PdfName.ROTATE) != null ? p.GetAsNumber(PdfName.ROTATE).IntValue : 0;
                p.Put(PdfName.ROTATE, new PdfNumber(Degrees));
            }
            Stamper.Outlines = SimpleBookmark.GetBookmark(r);
            Stamper.Close();
            r.Close();
            r = null;
            mem = null;
            SwapWorkingDoc();

        }
        private iTextSharp.text.Rectangle GetLandscapePageSize(PdfReader reader, int pagenumber)
        {
            iTextSharp.text.Rectangle pagesize = reader.GetPageSizeWithRotation(pagenumber);
            return new iTextSharp.text.Rectangle(
                    Math.Max(pagesize.Width, pagesize.Height),
                    Math.Min(pagesize.Width, pagesize.Height));
        }

        private iTextSharp.text.Rectangle GetPotraitPageSize(PdfReader reader, int pagenumber)
        {
            iTextSharp.text.Rectangle pagesize = reader.GetPageSizeWithRotation(pagenumber);
            return new iTextSharp.text.Rectangle(
                    Math.Min(pagesize.Width, pagesize.Height),
                    Math.Max(pagesize.Width, pagesize.Height));
        }
        private bool IsPortrait(PdfReader reader, int pagenumber)
        {
            iTextSharp.text.Rectangle pagesize = reader.GetPageSize(pagenumber);
            return pagesize.Height > pagesize.Width;
        }
        /// <summary>
        /// Get text that is contained within a rectangle on a page.
        /// </summary>
        /// <param name="PageNumber"></param>
        /// <param name="rect"></param>
        /// <returns></returns>
        public IList<HOcrClass> GetText(int PageNumber, BBox rect)
        {
            var r = GetReader();
            IList<HOcrClass> Elements = new List<HOcrClass>();
            PdfTextExtractionStrategy strat = new PdfTextExtractionStrategy();
            string te = PdfTextExtractor.GetTextFromPage(r, PageNumber, strat);

            if (rect.Format == UnitFormat.Pixel)
                rect = rect.DefaultPointBBox;
            rect.Top = r.GetPageSize(PageNumber).Height - rect.Top;
            foreach (var e in strat.Elements)
            {
                if (rect.Rectangle.Contains(e.BBox.Rectangle) || rect.Rectangle.IntersectsWith(e.BBox.Rectangle))
                    Elements.Add(e);

            }
            r.Close();
            r = null;
            return Elements;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="PageNumber"></param>
        /// <returns>Returns BBox values of found text in points, not pixels</returns>
        public IList<HOcrClass> GetWordLocations(int PageNumber)
        {
            var r = GetReader();
            IList<HOcrClass> Elements = new List<HOcrClass>();
            PdfTextExtractionStrategy strat = new PdfTextExtractionStrategy();
            string te = PdfTextExtractor.GetTextFromPage(r, PageNumber, strat);
            r.Close();
            r = null;
            return strat.Elements;
        }

        public hDocument ReconstructPageFromWordLocations(IList<HOcrClass> locations)
        {
            hDocument d = new hDocument();
            hPage g = new hPage();
            hParagraph p = new hParagraph();

            foreach (var c in locations.OrderByDescending(x => x.BBox.Top).ThenBy(x => x.BBox.Left))
            {
                if (p.Lines.Where(x => x.Words.Where(y => y.Id == c.Id).Count() > 0).Count() > 0)
                    continue;

                hLine l = new hLine();
                var words = locations.Where(x => Math.Abs(x.BBox.Top - c.BBox.Top) <= 1).ToList<HOcrClass>();

                foreach (var w in words)
                {
                    hWord hw = new hWord();
                    hw.Text = w.Text;
                    hw.Id = w.Id;
                    hw.BBox = w.BBox;
                    l.Text += w.Text;
                    l.Words.Add(hw);
                }

                BBox lb = new BBox();
                lb.Left = l.Words.OrderBy(x => x.BBox.Left).First().BBox.Left;
                lb.Top = l.Words.OrderBy(x => x.BBox.Top).First().BBox.Top;
                lb.Height = l.Words.OrderByDescending(x => x.BBox.Height).First().BBox.Height;
                lb.Width = l.Words.Select(x => x.BBox.Width).Sum();
                l.BBox = lb;
                l.Id = Guid.NewGuid().ToString();
                p.Lines.Add(l);
            }
            g.Paragraphs.Add(p);
            d.Pages.Add(g);

            return d;
        }
    }

    internal class PdfTextExtractionStrategy : LocationTextExtractionStrategy
    {
        public IList<HOcrClass> Elements { get; private set; }
        public PdfTextExtractionStrategy()
        {
            Elements = new List<HOcrClass>();
        }
        void BeginTextBlock() { }
        void EndTextBlock() { }
        void RenderImage(ImageRenderInfo info) { }
        int id = 0;
        public override void RenderText(TextRenderInfo info)
        {

            Vector bottomLeft = info.GetDescentLine().GetStartPoint();
            Vector topRight = info.GetAscentLine().GetEndPoint();
            iTextSharp.text.Rectangle rect = new iTextSharp.text.Rectangle(bottomLeft[Vector.I1],
                                           bottomLeft[Vector.I2],
                                           topRight[Vector.I1],
                                           topRight[Vector.I2]);

            id += 1;
            BBox b = new BBox();
            b.Format = UnitFormat.Point;
            b.Height = rect.Height;
            b.Left = rect.Left;
            b.Height = rect.Height;
            b.Width = rect.Width;
            b.Top = rect.Top;
            var o = new HOcrClass();
            o.BBox = b;
            o.Text = info.GetText();
            o.ClassName = "xocr_word";
            o.Id = id.ToString();
            Elements.Add(o);
            base.RenderText(info);
        }

    }
    internal class MyImageRenderListener : IRenderListener
    {
        public void RenderText(TextRenderInfo renderInfo) { }
        public void BeginTextBlock() { }
        public void EndTextBlock() { }

        public PDFDoc Reader { get; set; }
        public int PageNumber { get; set; }
        public List<ParsedPageImage> ParsedImages = new List<ParsedPageImage>();
        PdfName filter;

        public void RenderImage(ImageRenderInfo renderInfo)
        {
            PdfImageObject image = null;

            try
            {
                image = renderInfo.GetImage();
                if (image == null) return;

                if (renderInfo.GetRef() == null)
                    return;

                int num = renderInfo.GetRef().Number;


                byte[] bytes = image.GetImageAsBytes();
                if (bytes == null)
                    return;

                ParsedPageImage pi = new ParsedPageImage();
                pi.IndirectReferenceNum = num;
                pi.PdfImageObject = image;


                filter = (PdfName)image.Get(PdfName.FILTER);
                pi.Image = image.GetDrawingImage();
                //                if (filter.ToString() == "/DCTDecode")
                //                {
                //                    byte[] dbytes = Reader.ExtractPagesToPDFBytes(string.Concat(PageNumber, "-", PageNumber), "");
                //                    byte[] dimgBytes = MuPdfConverter.ConvertPdfToTiff(dbytes, 300, RenderType.RGB, false, false, 0, "");
                //                    System.Drawing.Image dcolor = System.Drawing.Image.FromStream(new MemoryStream(dimgBytes));
                //                    pi.Image = dcolor;
                //                    ParsedImages.Add(pi);
                //                    return;
                //                }
                //
                //                if (filter.ToString() == "/JBIG2Decode")
                //                {
                //                    byte[] imgBytes;
                //                    bytes = Reader.ExtractPagesToPDFBytes(string.Concat(PageNumber, "-", PageNumber), "");
                //                    imgBytes = MuPdfConverter.ConvertPdfToTiff(bytes, 300, RenderType.Grayscale, false, false, 0, "");
                //                    System.Drawing.Image grey = System.Drawing.Image.FromStream(new MemoryStream(imgBytes));
                //                    grey = ImageProcessor.ConvertToBitonal(ImageProcessor.GetAsBitmap(grey));
                //                    pi.Image = grey;
                //                    ParsedImages.Add(pi);
                //                    return;
                //                }

                using (MemoryStream ms = new MemoryStream(bytes))
                {
                    pi.Image = System.Drawing.Image.FromStream(new MemoryStream(bytes));
                    ParsedImages.Add(pi);
                    return;

                }
            }
            catch (IOException ie)
            {
                //PdfIndirectReference r = renderInfo.GetRef();


                //bytes = Reader.ExtractPagesToPDFBytes(string.Concat(PageNumber, "-", PageNumber), "");
                //imgBytes = MuPdfConverter.ConvertPdfToTiff(bytes, 300, RenderType.RGB, false,false, 0, "");
                //System.Drawing.Image color = System.Drawing.Image.FromStream(new MemoryStream(imgBytes));

                //Images.Add(color);
            }

        }
    }
    internal class ParsedPageImage
    {
        public System.Drawing.Image Image { get; set; }
        public PdfImageObject PdfImageObject { get; set; }
        public int IndirectReferenceNum { get; set; }
        public PRStream PRStream { get; set; }
    }
}
